package Java7_Concurrency_Cookbook.thread_synchronized_base.condition;

import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 数据缓冲类, 它将被生产者和消费者共享
 */
public class Buffer {
    private LinkedList<String> buffer;
    private int maxSize;
    private ReentrantLock lock;
    private Condition lines;
    private Condition space;
    /**
     * 用来表示缓冲区是否还有数据
     */
    private boolean pendingLines;

    public Buffer(int maxSize) {
        this.maxSize = maxSize;
        this.buffer = new LinkedList<>();
        this.lock = new ReentrantLock();
        this.lines = lock.newCondition();
        this.space = lock.newCondition();
        this.pendingLines = true;
    }

    public void insert(String line) {
        lock.lock();
        try {
            while (buffer.size() == maxSize) {
                space.await(); // 等待条件space的线程都将被挂起
            }
            buffer.offer(line);
            String tName = Thread.currentThread().getName();
            System.out.println(tName + " : Inserted Line : " + buffer.size());
            lines.signalAll(); // 向 lines 发送信号, 解除任何在 get() 方法中阻塞的线程
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public String get() {
        String line = null;
        lock.lock();
        try {
            while ((buffer.size() == 0) && (hasPendingLines())) {
                lines.await(); // buffer为空, 且缓冲区有数据, 等待并释放锁
            }
            if (hasPendingLines()) {
                line = buffer.poll();
                String tName = Thread.currentThread().getName();
                System.out.println(tName + " : Line Readed : " + buffer.size());
                space.signalAll(); // 向 space 发送信号, 解除任何在 insert() 方法中阻塞的线程
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return line;
    }

    public void setPendingLines(boolean pendingLines) {
        this.pendingLines = pendingLines;
    }

    public boolean hasPendingLines() {
        return pendingLines || buffer.size() > 0;
    }

}
